"""
备份服务 - 数据备份和恢复
"""
import os
import shutil
import logging
import subprocess
from datetime import datetime
from typing import Dict, Any, Optional
from pathlib import Path

from app.models.release import Release
from app.core.config import current_settings

logger = logging.getLogger(__name__)


class BackupService:
    """备份服务类"""
    
    def __init__(self):
        self.backup_dir = Path(current_settings.UPLOAD_DIR) / "backups"
        self.backup_dir.mkdir(parents=True, exist_ok=True)
    
    async def create_backup(self, release: Release) -> Dict[str, Any]:
        """创建备份"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_name = f"backup_{release.version}_{timestamp}"
            backup_path = self.backup_dir / backup_name
            
            # 创建备份目录
            backup_path.mkdir(exist_ok=True)
            
            # 备份数据库
            db_backup_result = await self._backup_database(backup_path)
            
            # 备份配置文件
            config_backup_result = await self._backup_configs(backup_path)
            
            # 备份应用数据
            data_backup_result = await self._backup_application_data(backup_path)
            
            # 创建备份元数据
            metadata = {
                "backup_name": backup_name,
                "release_version": release.version,
                "environment": release.environment,
                "created_at": datetime.now().isoformat(),
                "database_backup": db_backup_result,
                "config_backup": config_backup_result,
                "data_backup": data_backup_result
            }
            
            # 保存元数据
            await self._save_backup_metadata(backup_path, metadata)
            
            logger.info(f"备份创建成功: {backup_name}")
            
            return {
                "success": True,
                "backup_name": backup_name,
                "location": str(backup_path),
                "metadata": metadata
            }
            
        except Exception as e:
            logger.error(f"备份创建失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def restore_backup(self, backup_name: str, target_path: str = None) -> Dict[str, Any]:
        """恢复备份"""
        try:
            backup_path = self.backup_dir / backup_name
            if not backup_path.exists():
                raise ValueError(f"备份不存在: {backup_name}")
            
            # 读取备份元数据
            metadata = await self._load_backup_metadata(backup_path)
            
            # 恢复数据库
            db_restore_result = await self._restore_database(backup_path)
            
            # 恢复配置文件
            config_restore_result = await self._restore_configs(backup_path, target_path)
            
            # 恢复应用数据
            data_restore_result = await self._restore_application_data(backup_path, target_path)
            
            logger.info(f"备份恢复成功: {backup_name}")
            
            return {
                "success": True,
                "backup_name": backup_name,
                "restored_at": datetime.now().isoformat(),
                "database_restore": db_restore_result,
                "config_restore": config_restore_result,
                "data_restore": data_restore_result
            }
            
        except Exception as e:
            logger.error(f"备份恢复失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def list_backups(self) -> Dict[str, Any]:
        """列出所有备份"""
        try:
            backups = []
            for backup_dir in self.backup_dir.iterdir():
                if backup_dir.is_dir():
                    metadata_file = backup_dir / "metadata.json"
                    if metadata_file.exists():
                        metadata = await self._load_backup_metadata(backup_dir)
                        backups.append({
                            "name": backup_dir.name,
                            "version": metadata.get("release_version"),
                            "environment": metadata.get("environment"),
                            "created_at": metadata.get("created_at"),
                            "size": await self._get_backup_size(backup_dir)
                        })
            
            return {
                "success": True,
                "backups": sorted(backups, key=lambda x: x["created_at"], reverse=True)
            }
            
        except Exception as e:
            logger.error(f"获取备份列表失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def delete_backup(self, backup_name: str) -> Dict[str, Any]:
        """删除备份"""
        try:
            backup_path = self.backup_dir / backup_name
            if not backup_path.exists():
                raise ValueError(f"备份不存在: {backup_name}")
            
            # 删除备份目录
            shutil.rmtree(backup_path)
            
            logger.info(f"备份删除成功: {backup_name}")
            
            return {
                "success": True,
                "message": f"备份 {backup_name} 已删除"
            }
            
        except Exception as e:
            logger.error(f"备份删除失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _backup_database(self, backup_path: Path) -> Dict[str, Any]:
        """备份数据库"""
        try:
            db_backup_file = backup_path / "database.sql"
            
            # 根据数据库类型执行不同的备份命令
            if "postgresql" in current_settings.DATABASE_URL:
                # PostgreSQL备份
                cmd = [
                    "pg_dump",
                    "--dbname=" + current_settings.DATABASE_URL,
                    "--file=" + str(db_backup_file),
                    "--verbose"
                ]
            elif "mysql" in current_settings.DATABASE_URL:
                # MySQL备份
                cmd = [
                    "mysqldump",
                    "--result-file=" + str(db_backup_file),
                    "--verbose"
                ]
            else:
                # SQLite备份
                shutil.copy2(current_settings.DATABASE_URL.replace("sqlite:///", ""), db_backup_file)
                return {"success": True, "method": "file_copy"}
            
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.returncode == 0:
                return {
                    "success": True,
                    "method": "database_dump",
                    "file": str(db_backup_file)
                }
            else:
                return {
                    "success": False,
                    "error": result.stderr
                }
                
        except Exception as e:
            logger.error(f"数据库备份失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _backup_configs(self, backup_path: Path) -> Dict[str, Any]:
        """备份配置文件"""
        try:
            config_backup_dir = backup_path / "configs"
            config_backup_dir.mkdir(exist_ok=True)
            
            # 备份环境配置文件
            env_file = Path(".env")
            if env_file.exists():
                shutil.copy2(env_file, config_backup_dir / ".env")
            
            # 备份其他配置文件
            config_files = [
                "config.yaml",
                "config.json",
                "settings.py"
            ]
            
            copied_files = []
            for config_file in config_files:
                if Path(config_file).exists():
                    shutil.copy2(config_file, config_backup_dir / config_file)
                    copied_files.append(config_file)
            
            return {
                "success": True,
                "files": copied_files,
                "directory": str(config_backup_dir)
            }
            
        except Exception as e:
            logger.error(f"配置文件备份失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _backup_application_data(self, backup_path: Path) -> Dict[str, Any]:
        """备份应用数据"""
        try:
            data_backup_dir = backup_path / "data"
            data_backup_dir.mkdir(exist_ok=True)
            
            # 备份上传文件
            upload_dir = Path(current_settings.UPLOAD_DIR)
            if upload_dir.exists() and upload_dir != self.backup_dir:
                shutil.copytree(upload_dir, data_backup_dir / "uploads", dirs_exist_ok=True)
            
            # 备份日志文件
            log_dir = Path("logs")
            if log_dir.exists():
                shutil.copytree(log_dir, data_backup_dir / "logs", dirs_exist_ok=True)
            
            return {
                "success": True,
                "directory": str(data_backup_dir)
            }
            
        except Exception as e:
            logger.error(f"应用数据备份失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _restore_database(self, backup_path: Path) -> Dict[str, Any]:
        """恢复数据库"""
        try:
            db_backup_file = backup_path / "database.sql"
            if not db_backup_file.exists():
                return {
                    "success": False,
                    "error": "数据库备份文件不存在"
                }
            
            # 根据数据库类型执行不同的恢复命令
            if "postgresql" in current_settings.DATABASE_URL:
                # PostgreSQL恢复
                cmd = [
                    "psql",
                    "--dbname=" + current_settings.DATABASE_URL,
                    "--file=" + str(db_backup_file)
                ]
            elif "mysql" in current_settings.DATABASE_URL:
                # MySQL恢复
                cmd = [
                    "mysql",
                    "--execute=source " + str(db_backup_file)
                ]
            else:
                # SQLite恢复
                shutil.copy2(db_backup_file, current_settings.DATABASE_URL.replace("sqlite:///", ""))
                return {"success": True, "method": "file_copy"}
            
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.returncode == 0:
                return {
                    "success": True,
                    "method": "database_restore"
                }
            else:
                return {
                    "success": False,
                    "error": result.stderr
                }
                
        except Exception as e:
            logger.error(f"数据库恢复失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _restore_configs(self, backup_path: Path, target_path: str = None) -> Dict[str, Any]:
        """恢复配置文件"""
        try:
            config_backup_dir = backup_path / "configs"
            if not config_backup_dir.exists():
                return {
                    "success": False,
                    "error": "配置文件备份目录不存在"
                }
            
            target_dir = Path(target_path) if target_path else Path(".")
            
            restored_files = []
            for config_file in config_backup_dir.iterdir():
                if config_file.is_file():
                    shutil.copy2(config_file, target_dir / config_file.name)
                    restored_files.append(config_file.name)
            
            return {
                "success": True,
                "files": restored_files
            }
            
        except Exception as e:
            logger.error(f"配置文件恢复失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _restore_application_data(self, backup_path: Path, target_path: str = None) -> Dict[str, Any]:
        """恢复应用数据"""
        try:
            data_backup_dir = backup_path / "data"
            if not data_backup_dir.exists():
                return {
                    "success": False,
                    "error": "应用数据备份目录不存在"
                }
            
            target_dir = Path(target_path) if target_path else Path(".")
            
            restored_dirs = []
            for data_dir in data_backup_dir.iterdir():
                if data_dir.is_dir():
                    target_data_dir = target_dir / data_dir.name
                    shutil.copytree(data_dir, target_data_dir, dirs_exist_ok=True)
                    restored_dirs.append(data_dir.name)
            
            return {
                "success": True,
                "directories": restored_dirs
            }
            
        except Exception as e:
            logger.error(f"应用数据恢复失败: {e}")
            return {
                "success": False,
                "error": str(e)
            }
    
    async def _save_backup_metadata(self, backup_path: Path, metadata: Dict[str, Any]):
        """保存备份元数据"""
        import json
        
        metadata_file = backup_path / "metadata.json"
        with open(metadata_file, 'w', encoding='utf-8') as f:
            json.dump(metadata, f, indent=2, ensure_ascii=False)
    
    async def _load_backup_metadata(self, backup_path: Path) -> Dict[str, Any]:
        """加载备份元数据"""
        import json
        
        metadata_file = backup_path / "metadata.json"
        if not metadata_file.exists():
            return {}
        
        with open(metadata_file, 'r', encoding='utf-8') as f:
            return json.load(f)
    
    async def _get_backup_size(self, backup_path: Path) -> int:
        """获取备份大小"""
        total_size = 0
        for dirpath, dirnames, filenames in os.walk(backup_path):
            for filename in filenames:
                filepath = os.path.join(dirpath, filename)
                total_size += os.path.getsize(filepath)
        return total_size
